Skip to content

feat(loading-skeleton): constrain height and preserve clickability on mobile - #352

Merged
godamongstmen897 merged 130 commits into
Goldii-locks:mainfrom
otsimaofficial:feat/loading-skeleton-mobile-279
Aug 31, 2026
Merged

feat(loading-skeleton): constrain height and preserve clickability on mobile#352
godamongstmen897 merged 130 commits into
Goldii-locks:mainfrom
otsimaofficial:feat/loading-skeleton-mobile-279

Conversation

@otsimaofficial

Copy link
Copy Markdown
Contributor

Problem & Goal

On short mobile viewports, LoadingSkeleton's unconstrained height could push surrounding controls (role filters, pagination) far off-screen while a job's details were loading.

Changes

  • Wrapped the skeleton content in a new data-testid="loading-skeleton-mobile-wrapper" div: max-h-[70vh] overflow-y-auto overscroll-contain on mobile, sm:max-h-none to lift the cap on larger screens — it scrolls internally instead of growing the page.
  • Confirmed (and now test-covered) that the skeleton never uses fixed/absolute positioning, so it can't trap pointer events on other elements.

Testing

  • New __tests__/loading-skeleton-mobile.test.tsx (5 tests): height cap + scroll classes, the sm:max-h-none breakpoint, overscroll-contain, no fixed/absolute positioning, and — rendering the real Dashboard with a never-resolving fetch so the skeleton stays visible — that the role-filter buttons remain enabled and clickable while it's shown.
  • npx tsc --noEmit, the new test file, and npm run build all pass locally.

Note: npm run lint currently fails on this repo's main due to a pre-existing, unrelated react-hooks/set-state-in-effect error in SignatureTimeoutAlert.tsx (visible on the last several merged PRs' CI runs). That's fixed independently in a separate PR and isn't touched here.

Closes #279

🤖 Generated with Claude Code

Great-O and others added 30 commits August 25, 2026 17:06
…inner_skeleton

- Replace hardcoded gray-* Tailwind classes with semantic design tokens:
  * bg-gray-900  -> bg-surface-card
  * bg-gray-800  -> bg-surface-field (content placeholders)
  * border-gray-800 -> border-border-strong
  * bg-gray-700  -> bg-border-subtle (nested placeholders / contrast layer)
- Add data-testid attributes to all skeleton sections for testability
- Add 19 comprehensive test cases covering:
  * Design token validation (no hardcoded gray-* classes remain)
  * Correct token-to-element mapping across container/header/stats/milestones
  * Component layout structure (grid, count, padding, animations)
  * Accessibility attributes (role=status, aria-live, sr-only, aria-hidden)

All 436 tests (33 files) pass, including the 19 new assertions.
- Replace hardcoded empty message with EmptyStateCard component
- Display briefcase icon for job-related context
- Show descriptive title and explanation text
- Include role badges (Client, Freelancer, Arbiter) showing available participation options
- Add comprehensive test coverage with 16 test cases
- Ensure proper accessibility with region landmarks and aria-labels
- Validates placeholder display under empty data states
Implements the app dark/light theme toggle as a keyboard-operable,
ARIA-compliant switch (role=switch, aria-checked, aria-label) that
persists the chosen theme to localStorage and applies it to the document
root. Adds React Testing Library tests verifying node rendering,
accessible state, theme application, keyboard operation and persistence.

Closes Goldii-locks#319
Implements the navbar alert bell badge as a keyboard-operable
(role=button) disclosure with ARIA compliance (Goldii-locks#320): accessible name,
aria-haspopup/aria-expanded/aria-controls, aria-live announcement
regions, aria-hidden on decorative glyphs, focus-visible rings and
design-token contrast.

Adds field error indicators and alerts (Goldii-locks#324): validation field configs
render role=alert error text that toggles as validation triggers, wired
via aria-describedby and counted toward the unread badge.

Adds React Testing Library tests covering both requirements.

Closes Goldii-locks#320
Closes Goldii-locks#324
Stack the stat grid and content rows to a single column on mobile,
two on tablet, three on desktop, and use responsive padding/spacing
so LoadingSkeleton scales cleanly at every breakpoint.

Closes Goldii-locks#275
Fade the skeleton in on mount (animate-fade-in) instead of popping in
abruptly, and stagger each placeholder bar's pulse animation-delay so
the loading state reads as a smooth wave rather than a flat blink.

Closes Goldii-locks#278
Replace the bare "No jobs found" line with a reusable EmptyState
component (icon, heading, supporting copy) shown once loading
finishes with zero jobs for the connected wallet.

Closes Goldii-locks#276
godamongstmen897 and others added 28 commits August 31, 2026 12:46
…gas-warning

 feat: Add gas estimation warning banners to network_sync_checker
The PR imported EmptyStateCard but never added the component, so the
dashboard would not build. Added it, satisfying this PR's contract
(dashboard-empty-state test id, region role with an aria-label, an SVG
briefcase icon and role badges) while also carrying the shared
empty-state/-title/-description ids the existing dashboard empty-state
test queries.

The description now carries both wordings so the existing assertion
(/create one to get started/i) and this PR's fuller copy both hold.
…y-state-ui

feat: implement descriptive empty state UI for dashboard jobs list
…torybook-318

Build Storybook interface mocks for dark_mode_switcher
The branch was pushed with a botched conflict resolution: the `>>>>>>>`
markers were stripped but the branch-name lines and both sides of each
conflict were left in place. That left four stray
`Write-React-Testing-Library-assertions-...-Goldii-locks#242-FIX` lines, a duplicate
`@stellar/stellar-sdk` import block, a second `parseMessage` useMemo in
SignatureTimeoutAlert, and a duplicate `server` key in vitest.config.mts.

Resolved by taking main for the five files the branch only touched
incidentally -- main already carries the equivalent, deduplicated work
(the same parseMessage useMemo derivation and the same freighter-api
inline config the branch was re-adding).

The branch's actual deliverable for Goldii-locks#242 is kept intact:
WALLET_DISCONNECT_HANDLER_TESTS.md and the 851-line
wallet_disconnect_handler.component.test.ts.

lint 0 errors / tsc 0 errors / 1776 tests passing / build OK
…Testing-Library-assertions-for-wallet-disconnect-handler-#242-FIX

Write React Testing Library assertions for wallet disconnect handler …
…ns target

The two design-token cases rendered WalletSelectorModal with its default
props, where `errorMessage` defaults to null and `activeAddress` defaults
to null. Both surfaces under test are gated on those props, so
`wallet-selector-error-message` and `wallet-selector-connected-badge`
were never in the tree and the queries threw.

Passed an errorMessage to the first case and an activeAddress to the
second. The component's design tokens were already correct -- only the
test setup needed the props.

lint 0 errors / tsc 0 errors / 1778 tests passing / build OK
…e-modal-design-tokens

Feat: Dispute Raise ModaL
…n tokens

The suite hardcoded the raw Tailwind palette LoadingSkeleton used when
the branch was written. main has since migrated the component to
semantic design tokens, so the class assertions and the querySelector
calls that locate the cards no longer matched anything.

Remapped throughout:
  bg-gray-900                  -> bg-surface-card
  bg-gray-800 (bars, stat card)-> bg-surface-field
  bg-gray-700 (stat placehold.)-> bg-border-subtle
  border-gray-800              -> border-border-strong

Worth noting three cases were passing vacuously: the stat- and
milestone-card selectors returned empty NodeLists, so their forEach
assertion bodies never ran. With the selectors fixed they now iterate
and pass for real -- 54 of 54 in this file, up from 49.

lint 0 errors / tsc 0 errors / 1832 tests passing / build OK
…ng-spinner-skeleton

test: Add React Testing Library tests for LoadingSkeleton and ButtonSpinner (Goldii-locks#282)
… design tokens

The branch was cut before main migrated LoadingSkeleton to semantic
design tokens and added the skeleton-* test ids, so its side of the
conflict still carried bg-gray-*/no-testid markup. Resolved by applying
the branch's responsive layout on top of main's tokenised markup rather
than taking either side whole.

Two adjustments to keep the sibling suites passing:

- The branch's new test ids (loading-skeleton-card/-stats/-rows) name
  elements that already carry ids on main (skeleton-container,
  skeleton-stats-grid, skeleton-milestones). An element can only hold
  one data-testid, so the test now queries main's names -- every
  assertion is unchanged.

- Placeholder bars are expressed desktop-first (w-32 max-sm:w-24) rather
  than mobile-first (w-24 sm:w-32). Same computed result at both
  breakpoints, but it keeps the fixed widths on the element so the
  structural selectors in loading-spinner-skeleton.test.tsx still match.

The two card-padding assertions there did have to move to the responsive
class (p-6 -> sm:p-6, space-y-6 -> sm:space-y-6), since the branch
deliberately makes those breakpoint-dependent.

lint 0 errors / tsc 0 errors / 1841 tests passing / build OK
…skeleton-responsive-275

feat(loading-skeleton): responsive sizing across mobile/tablet/desktop
Same situation as Goldii-locks#349: the branch predates the design-token migration,
so its side of the LoadingSkeleton conflict still carried the old
bg-gray-* markup. Applied the branch's animation work on top of main's
current component instead of taking either side whole -- animate-fade-in
on the root, and animate-pulse plus the staggered
[animation-delay:...] utilities on the three stat cards (100/175/250ms)
and two milestone rows (325/400ms).

The branch's test ids (loading-skeleton-card/-stat-N/-row-N) name
elements that already carry ids on main, so the test queries main's
names (skeleton-container, skeleton-stat-card-N,
skeleton-milestone-card-N). Assertions and delay values are unchanged.

Delays are written as literal class names rather than interpolated,
since Tailwind only emits CSS for classes it can see statically.

lint 0 errors / tsc 0 errors / 1847 tests passing / build OK
…skeleton-animations-278

feat(loading-skeleton): CSS micro-animations on mount and pulse stagger
Same conflict shape as Goldii-locks#349 and Goldii-locks#351 -- the branch predates the
design-token migration, so its side of LoadingSkeleton still carried the
old bg-gray-* markup. Applied the branch's mobile work on top of main's
current component: a max-h-[70vh] sm:max-h-none overflow-y-auto
overscroll-contain wrapper between the status root and the card, so the
skeleton scrolls internally on small viewports rather than pushing the
surrounding controls off-screen.

The wrapper sits outside the card, so the card's first child is still
the header row that Goldii-locks#349's responsive assertions rely on.

lint 0 errors / tsc 0 errors / 1852 tests passing / build OK
@godamongstmen897
godamongstmen897 merged commit 204e882 into Goldii-locks:main Aug 31, 2026
1 check passed
godamongstmen897 added a commit to Great-O/escrow-frontend that referenced this pull request Aug 31, 2026
…he shared badge

This branch predates the LoadingSkeleton rewrite (Goldii-locks#349/Goldii-locks#351/Goldii-locks#352/Goldii-locks#354)
and the WalletBadge consolidation, and git merged it without conflicts
into something that does not parse -- the classic silent damage:

- LoadingSkeleton.tsx ended up as this branch's pre-token markup
  concatenated with main's current component, leaving an unterminated
  JSX block followed by `interface LoadingSkeletonProps`.
- WalletBadge.tsx ended up with two complete implementations: two
  `formatAddress` declarations and two `export default`s.
- Navbar.tsx and wallet_badge.test.tsx each got duplicate import lines.

LoadingSkeleton and SignatureTimeoutAlert are incidental to this PR --
its subject is the wallet badge -- and the branch's own copies were
already broken before the merge (its SignatureTimeoutAlert interleaves a
useEffect into the existing useMemo, leaving two catch blocks and an
undefined setParseMessage). Both were taken from main.

The badge work itself is real and entirely additive: 16 of its 33 cases
already passed against main's component, and the other 17 cover features
main did not have. Those were added to main's derived-status rendering
rather than replacing it:

  isValidStellarAddress   exported; G + 55 base32 chars, format only
  error / fieldError      wallet-field-error + wallet-error-text,
                          aria-invalid, red status dot
  validateAddress         flags a malformed address the same way
  alert                   wallet-alert-badge, amber dot; error wins
  empty placeholders      wallet-badge-placeholder, -custom and
                          wallet-empty-list-placeholder for empty
                          accounts/wallets/items, plus data-empty-state

One distinction worth noting: a present-but-blank address (`""`) is an
empty state, while a missing one (`null`) stays the ordinary
disconnected badge unless the caller passes placeholder copy. The
branch's own tests require both readings.

All four wallet-badge suites now pass together.

lint 0 errors / tsc 0 errors / 2052 tests passing / build OK
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Handle mobile viewports navigation styling in loading_spinner_skeleton